[Python] 関数定義時の特殊な引数宣言についてまとめ
こんにちは。サービス開発室の武田です。
Pythonでは、関数定義の引数にいくつかのバリエーションを持たせられます。ライブラリなどを作っている方は普段使いしている機能でしょうか。はじめて知った部分もありましたので、まとめてみました。
通常の引数
特段名称がないので「通常の」と書きましたが、何も指定せずに仮引数を宣言すると、Pythonでは位置引数とキーワード引数のどちらでも実引数を指定できます。位置引数とは実引数リストの宣言順に依存したもの、キーワード引数とはname=value
の形式で宣言したものです。キーワード引数は順序に依存しないため、変更に強いというメリットもあります。
def test1(a, b):
print(f"{a=}, {b=}")
test1(1, 2) # a=1, b=2
test1(b=1, a=2) # a=2, b=1
位置専用引数
英語ではPositional-Only Parameters
といいます。先ほど見たように特段指定なく仮引数を宣言した場合、位置引数とキーワード引数どちらでも指定できます。Python 3.8
から位置引数でしか指定できない引数を宣言できます。仮引数リストに/
を指定すると、それより前に置かれた仮引数は位置専用引数となります。
def test2(a, b, /):
print(f"{a=}, {b=}")
test2(1, 2) # a=1, b=2
test2(b=1, a=2) # TypeError
キーワード専用引数
英語ではKeyword-Only Arguments
といいます。先ほどは位置専用にする方法を見ましたが、キーワード専用の引数も宣言できます。Python 3.0
からサポートされており、仮引数リストに*
を指定すると、それより後に置かれた仮引数はキーワード専用引数となります。
def test3(*, a, b):
print(f"{a=}, {b=}")
test3(1, 2) # TypeError
test3(a=1, b=2) # a=1, b=2
デフォルト引数
基本的に仮引数リストに宣言された引数はすべて指定しないと関数を呼び出せません。Pythonでは引数にデフォルト値を指定でき、関数呼び出し時に省略した場合その値が使われます。なお指定する場合は引数リストの後ろから宣言する必要があります(デフォルト引数の後ろにデフォルトなしの引数は置けない)。
def test4(a, b=10):
print(f"{a=}, {b=}")
test4(1) # a=1, b=10
test4(1, 2) # a=1, b=2
デフォルト引数は関数ロード時に一度だけ初期化されます。そのため配列や辞書などミュータブルなオブジェクトを指定するとバグの元になります。そういった値は指定しないようにしましょう。
def test5(a=[]):
a.append(1)
print(a)
test5() # [1]
test5() # [1, 1]
代わりにこのように宣言します。
def test6(a=None):
if a is None:
a = []
a.append(1)
print(a)
test6() # [1]
test6() # [1]
可変長(位置)引数
0個以上の引数を受け取りたい場合、可変長引数が使用できます。*args
という形式で仮引数を宣言すると、呼び出し時に任意の個数の実引数を渡せます。可変長引数は慣例としてargs
という名前が使われますが自由です。なお引数はタプル型として受け取ります。
def test7(*args):
print(args)
test7() # ()
test7(1) # (1,)
test7(1, 2) # (1, 2)
可変長引数の後に、さらに仮引数を宣言できます。その場合、自動的にキーワード専用引数となります。
def test8(*args, a):
print(f"{args=}, {a=}")
test8(a=1) # args=(), a=1
test8(1, 2, a=3) # args=(1, 2), a=3
test8(1) # TypeError
可変長(キーワード)引数
先ほど可変長引数の使い方を紹介しましたが、受け取った値はタプル型でした。Pythonでは、タプル型ではなく辞書型として受け取ることもできます。キーワードの可変長引数は慣例としてkwargs
という名前が使われますが自由です。
def test9(**kwargs):
print(kwargs)
test9(1, 2) # TypeError
test9(a=1, b=2) # {'a': 1, 'b': 2}
引数を組み合わせてみる
デフォルト引数と可変長引数を組み合わせる関数はライブラリでよく見る形式です。
def test10(a=1, *args, **kwargs):
print(f"{a=}, {args=}, {kwargs=}")
test10(1, 2, n=10) # a=1, args=(2,), kwargs={'n': 10}
ちなみにこの宣言方式ですと、次の呼び出しはエラーになります。a
はkwargs
ではなく、第一引数の名前に引っ張られるためです。
test10(1, 2, a=10) # TypeError
これは、関数定義を次のように変更するとうまくいきます。
def test11(a=1, /, *args, **kwargs):
print(f"{a=}, {args=}, {kwargs=}")
test11(1, 2, a=10) # a=1, args=(2,), kwargs={'a': 10}
位置専用引数、キーワード専用引数、通常の引数、デフォルト引数を組み合わせることもできます。次の例では、a
が位置専用引数、b
が通常の引数、c
とd
がキーワード専用引数でd
はデフォルト値を持ちます。
def test12(a, /, b, *, c, d=None):
print(f"{a=}, {b=}, {c=}, {d=}")
test12(1, 2, c=3) # a=1, b=2, c=3, d=None
test12(1, 2, c=3, d=4) # a=1, b=2, c=3, d=4
test12(1, 2, 3, c=4) # TypeError
test12(1, b=2, c=3) # a=1, b=2, c=3, d=None
なおこの例は仮引数の正しい宣言順序となっています。箇条書きすると次のようになります。デフォルト引数はそれぞれの引数で(要件を満たせば)指定できます。
- 位置専用引数
- 通常の引数
- 可変長(位置)引数
- キーワード専用引数
- 可変長(キーワード)引数
まとめ
Pythonの仮引数宣言に関する機能をまとめてみました。可変長引数は利用する側としてもお世話になる機能ですね。普段使用しているライブラリなどでも、位置専用引数などが使用されているか確認してみます。